package com.ruoyi.common;

import com.ruoyi.common.domin.*;

import com.ruoyi.electronicFence.task.DeviceMonitor;
import org.apache.tomcat.util.buf.HexUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import struct.JavaStruct;

import java.nio.ByteOrder;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ClassName AgreementAnalysis
 * @Description 协议校验、解析、封装
 * @Author 张勇
 * @Date 2024-09-29
 * @Version 1.0.0.0
 **/

@Component
@DependsOn("deviceMonitor")
public class AgreementAnalysis {


    @Value("${system.logSwitch}")
    String SYSTEM_LOG_SWITCH;
    @Autowired
    private DeviceMonitor deviceMonitor;
    //=============================上行帧==============================================
    //分站上行帧固定头
    private final int[] upFrameFixedHeader = {0x44, 0x5A, 0x57, 0x4C};
    //数据类型长度
    private final int dataTypeNum = 1;
    //数据长度字节数
    private final int dataBytesNum = 2;
    //CRC校验字节数
    private final int crcCheckBytesNum = 2;
    //================================================================================


    /**
     * 校验和拆分上行帧
     */
    @Async
    public void analysisUpFrame() {
        while (true) {
            try {
                LocalDateTime now = LocalDateTime.now();
                int nano = now.getNano();
                int second = now.getSecond();
                if (second <1 && nano<100000000){
                    System.out.println("协议开始分析"+now);
                    System.out.println(nano);
                }
                LockConstants.PERSON_PROTOCOL_LOCK.lock();//解析和校验加锁
                Map<String, byte[]> socketMap = MainObject.tcpServer.getAddressMapBytes();
                //深表复制上行帧
                Map<String, byte[]> socketCloneMap = DeepCloneUtils.clone(socketMap);
                //清空当前socket会话数据
                socketMap.clear();
                LockConstants.PERSON_PROTOCOL_LOCK.unlock();//释放解析和校验锁
                //转换为迭代器
                Iterator<Map.Entry<String, byte[]>> iterator = socketCloneMap.entrySet().iterator();
                //循环解析数据
                while (iterator.hasNext()) {
                    Map.Entry<String, byte[]> entry = iterator.next();

                    //获取分站当前IP地址
                    String ipAddress = entry.getKey();
                    //获取当前会话数据
                    byte[] byteArray = entry.getValue();

                    //帧头中固定头起始位置，默认为0
                    int startFrameHeaderIndex = 0;
                    int i = 0;
                    //循环解析获取帧头（考虑粘包）
                    outer:
                    for (; i < byteArray.length; ) {
                        //定义当前位置
                        int currentPosition = i;
                        /**********************************************************
                         *每个字节数据，java中byte类型表示的数据范围为 -128到127
                         * 而在C语言中采用无符号ubyte类型表示数据范围为 0到255
                         * 所以java中只能将数据类型字节数放大来操作C语言中的
                         * 无符号字节类型，通过位与0xFF实现
                         *********************************************************/
                        int byteData;
                        int j = 0;
                        //循环解析及匹配帧头{0x44,0x5A,0x57,0x4C}
                        for (; j < upFrameFixedHeader.length; j++) {
                            byteData = byteArray[currentPosition++] & 0xFF;
                            if (byteData != upFrameFixedHeader[j]) {
                                i = i + 1;
                                continue outer;
                            }
                        }
                        //如果帧头中固定头匹配成功，记录当前固定头在整个数组中起始位置
                        if (j == upFrameFixedHeader.length) {
                            startFrameHeaderIndex = i;
                        }
                        //数据长度减去起始帧索引大于 固定头字节数+数据长度字节数
                        if ((byteArray.length - startFrameHeaderIndex) <= upFrameFixedHeader.length + dataTypeNum + dataBytesNum) {
                            break;
                        }

                        //数据长度字节数组
                        byte[] dataLengthBytes = TypeConvertUtils.getSubArray(byteArray, startFrameHeaderIndex + upFrameFixedHeader.length + dataTypeNum, dataBytesNum);
                        //数据长度（注意小端传输）
                        int dataLength = TypeConvertUtils.bytesToReverseInt(dataLengthBytes);

                        //数据长度减去起始帧索引大于 固定头字节数+数据长度字节数+数据长度+CRC16校验字节数
                        if ((byteArray.length - startFrameHeaderIndex) <  dataLength + crcCheckBytesNum) {
                            break;
                        }

                        //截取帧头+数据
                        byte[] frameHeaderAndData = TypeConvertUtils.getSubArray(byteArray, startFrameHeaderIndex, dataLength);
                        //crc16 校验结果
                        int crcResult = calculateCRC16(frameHeaderAndData);
                        //截取CRC16校验字节数组
                        byte[] crcCheckBytes = TypeConvertUtils.getSubArray(byteArray, startFrameHeaderIndex + dataLength, crcCheckBytesNum);
                        //计算CRC16计算结果
                        int crcCheckData = TypeConvertUtils.bytesToReverseInt(crcCheckBytes);
                        //循环遍历索引改变
                        i = i  + dataLength + crcCheckBytesNum;
                        //判断CRC16校验是否正确，如果不正确则丢弃该帧
                        if (crcCheckData != crcResult) {
                            System.out.println(HexUtils.toHexString(byteArray));
                            //记录丢弃日志
                            continue;
                        } else {//如果校验成功
                            //截取类型
                            byte[] dataTypeBytes = TypeConvertUtils.getSubArray(byteArray, startFrameHeaderIndex + upFrameFixedHeader.length, dataTypeNum);
                            //数据帧类型
                            int dataType = dataTypeBytes[0] & 0xFF;
                            //数据字节数组
                            byte[] dataBytes = TypeConvertUtils.getSubArray(byteArray, startFrameHeaderIndex + upFrameFixedHeader.length +dataTypeNum + dataBytesNum, dataLength);
                            //根据数据类型匹配数据
                            switch (dataType) {
                                case 0x01://信息上传帧
                                    fullUpInformationPackage(dataBytes);
                                    break;

                                default:
                                    break;
                            }
                        }
                    }
                }
                //线程休眠50毫秒
                Thread.sleep(50);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }


    /**
     * 填充简易数据
     * @param subBytes
     */
    private void fullUpInformationPackage(byte[] subBytes) {
        try {
            UpInformationPackage upInformationPackage = new UpInformationPackage();
            JavaStruct.unpack(upInformationPackage, subBytes, ByteOrder.LITTLE_ENDIAN);

            MainObject.TEST_INSERT_LOCK.lock();
            ConcurrentHashMap<String, Object> temp = new ConcurrentHashMap<>();
            temp.put("stationIp", upInformationPackage.getStationIp());
            temp.put("stationClock", upInformationPackage.getStationClock());
            temp.put("controlState", upInformationPackage.getControlState());
            temp.put("deviceState", upInformationPackage.getDeviceState());
            temp.put("alarmReason", upInformationPackage.getAlarmReason());
            temp.put("overhaulModel", upInformationPackage.getOverhaulModel());
            temp.put("inputOne", upInformationPackage.getInputOne());
            temp.put("inputTwo", upInformationPackage.getInputTwo());
            temp.put("inputThree", upInformationPackage.getInputThree());
            temp.put("inputFour", upInformationPackage.getInputFour());
            temp.put("inputFive", upInformationPackage.getInputFive());
            temp.put("inputSix", upInformationPackage.getInputSix());
            temp.put("inputSeven", upInformationPackage.getInputSeven());
            temp.put("inputEight", upInformationPackage.getInputEight());


            LocalDateTime now = LocalDateTime.now();
            int nano = now.getNano();
            int second = now.getSecond();

            if (second % 5 == 0 ) {
                if ("true".equals(SYSTEM_LOG_SWITCH)) {
                    System.out.println(temp);
                }
                System.out.println("数据接收入库"+now);
                System.out.println(nano);
                HashMap<String, Object> map = new HashMap<>();
                map.put("Result","Succeed");
                map.put("controllerStatus",temp.get("controlState"));
                map.put("controllerIP",temp.get("stationIp"));
                map.put("alarmTime",temp.get("stationClock"));
                map.put("pictureUrl","");
                map.put("alarmType","");
                map.put("equipmentStatus","");
                deviceMonitor.dataConversion(map);
//                MainObject.LIST_REAL_TEST.add(temp);
                System.out.println("MainObject.LIST_REAL_TEST    "+MainObject.LIST_REAL_TEST.size());
            }
            System.out.println("MainObject.TEST_INSERT_LOCK"+MainObject.TEST_INSERT_LOCK);
            MainObject.TEST_INSERT_LOCK.unlock();

            //TODO
//            if ("true".equals(SYSTEM_LOG_SWITCH)){
//                System.out.println(temp);
//            }
//            System.out.println(temp);
//            HashMap<String, Object> map = new HashMap<>();
//            map.put("Result","Succeed");
//            map.put("controllerStatus",temp.get("controlState"));
//            map.put("controllerIP",temp.get("stationIp"));
//            map.put("alarmTime",temp.get("stationClock"));
//            map.put("pictureUrl","");
//            map.put("alarmType","");
//            map.put("equipmentStatus","");

//            deviceMonitor.dataConversion(map);
////            deviceMonitor.
//            MainObject.LIST_REAL_TEST.add(temp);
//            MainObject.TEST_INSERT_LOCK.unlock();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 计算CRC16
     * @param bytes 需要计算CRC16的字节数组
     * @return 计算出的CRC16值
     */
    public static int calculateCRC16(byte[] bytes) {
        int crc = 0xFFFF; // 初始值
        for (int i = 0; i < bytes.length; i++) {
            crc ^= bytes[i] & 0xFF;
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x01) == 1) {
                    crc = (crc >> 1) ^ 0xA001; // 生成多项式
                } else {
                    crc = crc >> 1;
                }
            }
        }
        return crc;
    }
}
